package com.ruoyi.web.util;

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

import java.io.IOException;
import java.io.StringReader;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.util.Base64;

public class SM2Utils {

    private static final String SM2PrivateKey = "1e78a7ff93a1d5d2fc715c6ab9334ac81abb98a13b55061d167cd199626c71d3";

    // SM2 算法 OID: 1.2.156.10197.1.301
    private static final ASN1ObjectIdentifier SM2_OID = new ASN1ObjectIdentifier("1.2.156.10197.1.301");

    /**
     * 加载私钥
     */
    public static PrivateKey loadPrivateKey(String pemPrivateKey) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
//        // 显式加载 SM2 曲线参数
//        ECNamedCurveParameterSpec sm2Spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
//        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
//        kpg.initialize(sm2Spec);
//        pemPrivateKey = "-----BEGIN PRIVATE KEY-----\n" + pemPrivateKey + "\n-----END PRIVATE KEY-----";
        pemPrivateKey = convertHexToPkcs8Pem(pemPrivateKey);
        PEMParser pemParser = new PEMParser(new StringReader(pemPrivateKey));
        Object object = pemParser.readObject();
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) object;
        return converter.getPrivateKey(privateKeyInfo);
    }

    /**
     * 解密
     */
    public static byte[] decrypt(byte[] cipherText, BCECPrivateKey privateKey) throws Exception {
        // 初始化SM2引擎，使用C1C3C2模式（根据加密设置调整）
        SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);

        // 转换私钥为ECPrivateKeyParameters
        ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters)
                PrivateKeyFactory.createKey(privateKey.getEncoded());

        sm2Engine.init(false, privateKeyParameters); // false表示解密

        // 执行解密
        return sm2Engine.processBlock(cipherText, 0, cipherText.length);
    }

    public static String getSM2PrivateKey() {
        return SM2PrivateKey;
    }

    /**
     * 将十六进制私钥字符串转换为 PKCS#8 PEM 格式
     * @param hexPrivateKey 32字节的十六进制私钥字符串（如 "1e78a7ff..."）
     * @return PEM 格式的私钥字符串
     */
    public static String convertHexToPkcs8Pem(String hexPrivateKey) throws IOException {
        // 1. 将十六进制字符串转为字节数组
        byte[] privateKeyBytes = hexStringToBytes(hexPrivateKey);

        // 2. 构造 ASN.1 结构
        ASN1EncodableVector pkcs8Sequence = new ASN1EncodableVector();
        pkcs8Sequence.add(new ASN1Integer(0)); // 版本号 0

        // 算法标识符（SM2 OID）
        ASN1EncodableVector algorithmId = new ASN1EncodableVector();
        algorithmId.add(SM2_OID);
        pkcs8Sequence.add(new DERSequence(algorithmId));

        // 私钥数据（OCTET STRING）
        pkcs8Sequence.add(new DEROctetString(privateKeyBytes));

        // 3. 生成 DER 编码
        byte[] derEncoded = new DERSequence(pkcs8Sequence).getEncoded("DER");

        // 4. Base64 编码并包装为 PEM
        return "-----BEGIN PRIVATE KEY-----\n" +
                insertLineBreaks(Base64.getEncoder().encodeToString(derEncoded)) +
                "\n-----END PRIVATE KEY-----";
    }

    // 十六进制字符串转字节数组
    private static byte[] hexStringToBytes(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i+1), 16));
        }
        return data;
    }

    // Base64 每行 64 字符分割
    private static String insertLineBreaks(String base64) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < base64.length(); i += 64) {
            sb.append(base64, i, Math.min(i + 64, base64.length()));
            sb.append("\n");
        }
        return sb.toString().trim();
    }
}
